home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------------------------------------------
-
- Lurkers
-
- Greg Anderson
- January 1994
-
- AppleLink: G.ANDERSON
- Internet: greggor@apple.com
-
- Modified by Greg Branche, gregb@apple.com
- October 1997
-
- About Lurkers:
-
- Lurkers searches a directory for files that are modifiable, including files
- not in a project, files checked out for modification, and modify-read-only
- files.
-
- Search modes (pick any one):
-
- -modifiable (default) Search for files not in a project, files
- checked out for modification, and modify-read-only files
- -notmodifiable Search for files checked into a project that are not
- modifiable (has 'ckid', but is neither MRO nor checked out
- for modification)
- -mro Search for files that are checked into a project (has 'ckid'),
- but have been modified-read-only.
- -insomeproject Search for files with a 'ckid' resource--modifiable or not
- -notinanyproject Search for files without a 'ckid' resource
- -modifiableinproject Search for files with a 'ckid' resource that are MRO'ed or
- checked out for modification (does not actually check
- project database to see if any changes have been made, though)
- -checkedoutmodifiable Searches for files with a 'ckid' resource that are
- checked out for modification.
-
- Other flags:
-
- -r Search specified directory recursively
- -s Short output (only list names of files that match
- the search criterion)
- -f Output full pathnames
- -q Don't quote filenames with special characters
- (Quoting only done in short output)
- -p Show progress information
- -rev Show the revision number of the file
- -textonly Ignore files whose type is not TEXT
- -ignore n Ignore files and folders whose name is 'n'
-
- Revision History:
-
- 1.0, 19 Jan 94 Initial release
- 1.1, 20 Jan 94 Lurkers being too strict in its test for checked out
- files, so it missed some. Also, I failed to check
- for errors when opening the resource fork of a file
- (oops!), so files without a resource fork reported
- the information applicable to the next item in the
- resource chain (not a problem unless you check your
- MPW shell into a project--but our team does!)
- 1.2, 25 Jan 94 Lurkers still missing files; make its test even less
- strict (pretty sure I got it right this time)
- 1.3, 27 Jan 94 Added flags -f -q -rev -textonly
- 1.4, 16 Feb 94 Added partial pathnames, -checkedoutmodifiable, -mro
- 1.5, 2 Jun 94 Andy wanted me to remove some spurrious blank lines in -s mode
- 1.6, 9 Jun 94 Improved help, added -ignore, allowed single files to
- be tested
- 1.7, 7 Oct 97 Added support for all special shell characters that must be quoted.
- Did one of the "To Do"s that was left over from previous versions. (i.e.,
- quoting of single quotes within a name is handled properly)
-
- To Do:
-
- It would also be nice if -ignore handled wildcards, or even regular expresions
-
- ----------------------------------------------------------------------------------------*/
-
- #include <Types.h>
- #include <ctype.h>
- #include <fcntl.h>
- #include <string.h>
- #include <stdio.h>
- #include <StdLib.h>
- #include <ErrMgr.h>
- #include <CursorCtl.h>
- #include <Errors.h>
- #include <QuickDraw.h>
- #include <Files.h>
- #include <Memory.h>
- #include <Resources.h>
- #include <CursorCtl.h>
- #include <PLStringFuncs.h>
-
- //
- // First line of usage string (more than this is printed out)
- //
- static char* usage = "%s [file's projector status] [option…] directory|file…\n";
-
- static long optionsSpecified = false;
-
- //
- // We know a little bit about the contents of a ckid resource:
- //
- struct ckidheader
- {
- long fUnknownID; // Different after every projector operation
- long fUnkownConstant; // Always the same
-
- short fUnknown1; // Usually 0004
- short fIsCheckedOut; // Usually 2, 3 or 4 if checked out, always 0 if not
-
- short fIsMRO; // 0001 if MRO, 0000 if not
- short fUnknown2; // Usually 0000
-
- long fUnknown3; // I don't know or care what these are
- long fUnknown4;
- long fUnknown5;
- long fUnknown6;
- long fUnknown7;
- long fUnknown8;
-
- unsigned char fProjectName; // null-terminated pascal string
-
- // After the project name is the name of the
- // person who checked out this file on this
- // machine (no relation to the person who currently
- // has the file checked out). Also null-terminated
-
- // After the user name is the revision number
- // of the file, in ascii and null-terminated.
-
- };
-
- //
- // Projector states that we know about:
- // (One and only one of these bits may be set at any time, as we switch on them)
- //
- #define kNotModifiable 1
- #define kModifiedReadOnly 2
- #define kCheckedOut 4
- #define kNotInAProject 8
-
- //
- // This mask must include all of the file project states above
- //
- #define kFileProjectMask (kNotModifiable | kModifiedReadOnly | kCheckedOut | kNotInAProject)
-
- //
- // Here are some extra file project states that we do not switch on
- // (so these bits may be set in addition to the ones above)
- //
- #define kHasCKIDAndIsModifiable 0x10
- #define kHasCKIDAndIsCheckedOut 0x20
-
- //
- // Global options, because there is no need to pass it around
- //
- long gOptions = 0;
- long gWantOutputForState = kModifiedReadOnly | kCheckedOut | kNotInAProject;
-
- #define kShortOutput 1
- #define kRecursive 2
- #define kOutputRevision 4
- #define kTextOnly 8
- #define kFullPathnames 0x10
- #define kDontQuote 0x20
- #define kShowProgress 0x80000000
-
- #define kMagicUnusedDirID 0x8000000
-
- typedef enum {
- noQuoting, standardQuoting, specialQuoting
- } QuoteType;
-
- //
- // Some global variables that should be local, but I don't
- // want my stack to get too large, and I don't feel like
- // being clever.
- //
- // The code is very carefully written to avoid using these
- // variables after they have been reused in a recursive call.
- // This is EVIL, but we want to avoid having a large stack.
- //
- CInfoPBRec pb;
- Str255 gFilename;
- Str255 gFolderpath;
-
- Str255 gTempProjectName;
- Str31 gTempRevisionNumber;
- char gTempRevisionString[64];
-
- char** gIgnoreList;
- short gNumberOfIgnoreItems = 0;
-
- #define ConvertToUppercase(c) (((c >= 'a') && (c <= 'z')) ? c - 'a' + 'A' : c)
-
- //----------------------------------------------------------------------------------------
- // OptionSpecified:
- //----------------------------------------------------------------------------------------
- Boolean OptionSpecified(long flag)
- {
- return ((gOptions & flag) != 0);
- } // OptionSpecified
-
- //----------------------------------------------------------------------------------------
- // ItemMatches
- //
- // It would be nice if this checked for wildcards...
- //----------------------------------------------------------------------------------------
- Boolean ItemMatches(Str255 testItem, char* compareWith)
- {
- Boolean itemMatches = true;
- short i;
-
- for(i=1; i<=testItem[0]; ++i)
- {
- unsigned char c1 = testItem[i];
- unsigned char c2 = *compareWith++;
- if(ConvertToUppercase(c1) != ConvertToUppercase(c2))
- {
- itemMatches = false;
- break;
- }
- }
-
- //
- // If we ran out of characters in 'testItem', but there
- // are still characters left in 'compareWith', then the
- // two items don't really match
- //
- if(*compareWith != 0)
- itemMatches = false;
-
- return itemMatches;
- } // ItemMatches
-
- //----------------------------------------------------------------------------------------
- // ItemIsInIgnoreList
- //
- // Return 'true' if the specified string is in the ignore list
- //----------------------------------------------------------------------------------------
- Boolean ItemIsInIgnoreList(Str255 testItem)
- {
- Boolean shouldIgnore = false;
- short i;
-
- for(i=0; i< gNumberOfIgnoreItems; ++i)
- {
- if(ItemMatches(testItem, gIgnoreList[i]))
- {
- shouldIgnore = true;
- break;
- }
- }
-
- return shouldIgnore;
- } // ItemIsInIgnoreList
-
- //----------------------------------------------------------------------------------------
- // BuildFolderPathname:
- //----------------------------------------------------------------------------------------
- void BuildFolderPathname(short vRefNum, long dirID, Str255 folderpath, long stopDirID, Boolean firstTime)
- {
- Str63 thisName;
- OSErr err = noErr;
-
- thisName[0] = 0;
- folderpath[0] = 0;
-
- pb.dirInfo.ioCompletion = nil;
- pb.dirInfo.ioNamePtr = thisName;
- pb.dirInfo.ioResult = noErr;
- pb.dirInfo.ioVRefNum = vRefNum;
- pb.dirInfo.ioDrDirID = dirID;
- pb.dirInfo.ioFDirIndex = -1;
-
- err = PBGetCatInfo(&pb,false);
-
- if(err == noErr)
- {
- if(dirID != stopDirID)
- {
- BuildFolderPathname(vRefNum, pb.dirInfo.ioDrParID, folderpath, stopDirID, false);
-
- //
- // Append 'thisName' onto folderpath
- //
- thisName[thisName[0] + 1] = 0;
- strcpy((char *) folderpath + folderpath[0] + 1, (char *) thisName + 1);
- folderpath[0] += thisName[0];
-
- //
- // Append a colon onto folderpath
- //
- folderpath[folderpath[0] + 1] = ':';
- ++folderpath[0];
- }
- else if(!firstTime)
- {
- folderpath[1] = ':';
- folderpath[0] = 1;
- }
- }
- } // BuildFolderPathname
-
- //----------------------------------------------------------------------------------------
- // ClassifyCharacter:
- //----------------------------------------------------------------------------------------
- QuoteType ClassifyCharacter(char c)
- {
- switch(c)
- {
- case '\t': // Tab also separates words.
- case '\n': // Return separates commands.
- case '\'': // Single quote quotes all other characters.
- return(specialQuoting);
- break;
-
- case ' ': // Space separates words.
- case ';': // Semicolon also separates commands.
- case '|': // Pipe separates commands and pipes output to input.
- case '&': // And separates commands, executing second if first succeeds.
- case '(': //
- case ')': // Parenthesis group commands.
- case '#': // Comment begins comments.
- case '\"': // Double quote quotes all characters except ∂, {, and `.
- case '∂': // Escape (Option-D) quotes the following character.
- case '/': // Slash quotes all characters except ∂, {, and `.
- case '\\': // Backslash quotes all characters except ∂, {, and `.
- case '{':
- case '}': // Braces denote variable substitution.
- case '`': // Backquotes denote command substitution.
- case '\?': // Question mark matches any character in filename patterns.
- case '≈': // Approximately (Option-X) matches any string in patterns.
- case '[':
- case ']': // Brackets enclose character sets in filename patterns.
- case '*': // Star indicates zero or more repetitions in patterns.
- case '+': // Plus indicates one or more repetitions in patterns.
- case '«':
- case '»': // European quotes (Option-\ and Option-Shift-\) enclose repeat counts.
- case '<': // Less-than indicates an input file specification.
- case '>': // Greater-than indicates an output file specification.
- case '≥': // Greater-than-or-equal indicates a diagnostic specification.
- case '∑': // Capital sigma (option-w) indicates both an output file and diagnostic output file specification.
- case '…': // Ellipsis (Option-;) signals the Shell to use Commando
- return(standardQuoting);
- break;
-
- default:
- return(noQuoting);
- }
-
- return(noQuoting); // we gotta return _something_!
- } // ClassifyCharacter
-
- //----------------------------------------------------------------------------------------
- // HasSpecialCharacters:
- //----------------------------------------------------------------------------------------
- QuoteType HasSpecialCharacters(Str255 pstring)
- {
- QuoteType hasSpecial = noQuoting;
- short i;
-
- for(i = 1; i <= pstring[0]; ++i)
- {
- QuoteType isSpecial = ClassifyCharacter(pstring[i]);
-
- if(isSpecial > hasSpecial)
- if((hasSpecial = isSpecial) == specialQuoting)
- break; // no reason to continue searching, if we've already found the most restrictive character type
- }
-
- return hasSpecial;
- } // HasSpecialCharacters
-
- //----------------------------------------------------------------------------------------
- // QuoteString:
- //----------------------------------------------------------------------------------------
- void QuoteString(Str255 theString, Boolean *withinQuotes)
- {
- int i;
- char toPrint;
-
- for(i = 1; i <= theString[0]; ++i)
- {
- if(ClassifyCharacter(theString[i]) == specialQuoting)
- {
- if(*withinQuotes)
- {
- putchar('\'');
- *withinQuotes = false;
- }
- switch(theString[i])
- {
- case '\t': toPrint = 't'; break; // Tab also separates words.
- case '\n': toPrint = 'n'; break; // Return separates commands.
- case '\'': toPrint = '\''; break; // Single quote quotes all other characters.
- default: toPrint = theString[i];
- }
- fprintf(stdout, "∂%c", toPrint);
- }
- else
- {
- if(!*withinQuotes)
- {
- putchar('\'');
- *withinQuotes = true;
- }
- putchar(theString[i]);
- }
- }
- } // QuoteString
-
- //----------------------------------------------------------------------------------------
- // ExamineProjectorInformation:
- //----------------------------------------------------------------------------------------
- long ExamineProjectorInformation(short vRefNum, long dirID, const Str255 fileName, Str255 projectName, Str31 revisionNumber)
- {
- Handle ckidHandle = nil;
- short resRefNum;
- long projectorInfo = kNotInAProject;
-
- projectName[0] = 0;
- revisionNumber[0] = 0;
-
- resRefNum = HOpenResFile(vRefNum, dirID, fileName, fsRdPerm);
-
- if(resRefNum != -1)
- {
- ckidHandle = Get1Resource('ckid', 128);
- if(ckidHandle != nil)
- {
- struct ckidheader* ckpeek = nil;
- unsigned char* p;
-
- HLock(ckidHandle);
- ckpeek = *( (struct ckidheader**)ckidHandle);
-
- //
- // Look at a bit of the ckid resource
- //
- if(ckpeek->fIsCheckedOut != 0)
- projectorInfo = kCheckedOut;
- else if((ckpeek->fIsMRO & 1) != 0)
- projectorInfo = kModifiedReadOnly;
- else
- projectorInfo = kNotModifiable;
-
- //
- // Set the 'changed' bit if this item
- // is modifiable (we know that it is in a project)
- //
- if((projectorInfo & (kModifiedReadOnly | kCheckedOut)) != 0)
- projectorInfo |= kHasCKIDAndIsModifiable;
- if((projectorInfo & kCheckedOut) != 0)
- projectorInfo |= kHasCKIDAndIsCheckedOut;
-
- //
- // Hack: 'projectName' and fProjectName are
- // pascal strings, but they are always
- // null-terminated, so we can strcpy it
- //
- strcpy((char *) projectName, (char *) &ckpeek->fProjectName);
-
- //
- // The string past the project name is the user name
- // (add one to skip the null termination)
- //
- p = &ckpeek->fProjectName + strlen((char *) &ckpeek->fProjectName) + 1;
-
- //
- // The string past the user name is the revision number
- // (add one to skip the null termination)
- // Once again, we use strcpy, as the string is always
- // null-terminated.
- //
- p = p + strlen((char *) p) + 1;
- strcpy((char *) revisionNumber, (char *) p);
-
- HUnlock(ckidHandle);
- }
-
- CloseResFile(resRefNum);
- }
- //else
- // fprintf(stderr, "### Error opening resource fork of %P", fileName);
-
- return projectorInfo;
- } // ExamineProjectorInformation
-
- //----------------------------------------------------------------------------------------
- // ProcessFile:
- //----------------------------------------------------------------------------------------
- void ProcessFile(short vRefNum, long dirID, Str255 folderpath, Str255 filename, Boolean* didOutput)
- {
- char commaString[4];
- long fileProjectStatus = 0;
-
- //
- // Don't do a thing with this item if it's in the ignore list
- //
- if(ItemIsInIgnoreList(filename) == false)
- {
- //
- // Find out if the file has a 'ckid' resource. While we're at it,
- // extract information about the file (checked out, MRO'ed, etc, project
- // name, revision number)
- //
- fileProjectStatus = ExamineProjectorInformation(vRefNum, dirID, filename, gTempProjectName, gTempRevisionNumber);
-
- //
- // Ignore the file unless we want output for its state
- //
- if((gWantOutputForState & fileProjectStatus) != 0)
- {
- //
- // If doing short output (-s), only print the name of the file
- //
- if(OptionSpecified(kShortOutput))
- {
- //
- // Print revision numbers if requested (-rev)
- //
- if((OptionSpecified(kOutputRevision)) && (gTempRevisionNumber[0] != 0))
- {
- strcpy(commaString, ",");
- }
- else
- {
- commaString[0] = 0;
- gTempRevisionNumber[0] = 0;
- }
-
- //
- // Quote the string if necessary
- //
- if(OptionSpecified(kDontQuote) == true)
- {
- fprintf(stdout, "%P%P%s%P\n", folderpath, filename, commaString, gTempRevisionNumber);
- }
- else
- {
- QuoteType pathQuotingNeeded, fileQuotingNeeded;
-
- pathQuotingNeeded = HasSpecialCharacters(folderpath);
- fileQuotingNeeded = HasSpecialCharacters(filename);
-
-
- if(pathQuotingNeeded == noQuoting && fileQuotingNeeded == noQuoting)
- { // no special characters found, just print the darn thang!
- fprintf(stdout, "%P%P%s%P\n", folderpath, filename, commaString, gTempRevisionNumber);
- }
- else if(pathQuotingNeeded < specialQuoting && fileQuotingNeeded < specialQuoting)
- { // we can just quote the whole pathname...
- fprintf(stdout, "'%P%P%s%P'\n", folderpath, filename, commaString, gTempRevisionNumber);
- }
- else
- { // Here, we have to parse the string to be printed, so that the proper single-character quoting can be emitted
- Boolean withinQuotes = false;
-
- // First, the folder portion of the pathname...
- QuoteString(folderpath, &withinQuotes);
-
- // then the filename
- QuoteString(filename, &withinQuotes);
-
- // finish up...
- if(gTempRevisionNumber[0] != 0)
- {
- if(!withinQuotes)
- {
- putchar('\'');
- withinQuotes = true;
- }
- fprintf(stdout, "%s%P", commaString, gTempRevisionNumber);
- }
- if(withinQuotes)
- {
- putchar('\'');
- withinQuotes = false;
- }
- putchar('\n');
- }
- }
-
- }
- else
- {
- if(OptionSpecified(kOutputRevision))
- sprintf(gTempRevisionString, "(%P,%P) ", filename, gTempRevisionNumber);
- else
- gTempRevisionString[0] = 0;
-
- if(PLstrchr(folderpath, '\n') != NULL || PLstrchr(filename, '\n') != NULL)
- {
- Boolean inQuotes = true;
- fputs("File \'", stdout);
- QuoteString(folderpath, &inQuotes);
- QuoteString(filename, &inQuotes);
- if(inQuotes)
- fputc('\'', stdout);
- fputs("; # ", stdout);
- }
- else
- {
- fprintf(stdout, "File \"%P%P\"; # ", folderpath, filename);
- }
-
- switch(fileProjectStatus & kFileProjectMask)
- {
- case kNotInAProject:
- fprintf(stdout, "is not in a project\n");
- break;
-
- case kNotModifiable:
- fprintf(stdout, "%sis an unmodifiable copy of a file in the project %P\n", gTempRevisionString, gTempProjectName);
- break;
-
- case kModifiedReadOnly:
- fprintf(stdout, "%sis a modify-read-only copy of a file in the project %P\n", gTempRevisionString, gTempProjectName);
- break;
-
- case kCheckedOut:
- fprintf(stdout, "%sis a modifiable copy of a file in the project %P\n", gTempRevisionString, gTempProjectName);
- break;
-
- default:
- fprintf(stdout, "%sis not recognized by Lurkers\n", gTempRevisionString);
- break;
- }
-
- //
- // didOutput actually means "printed a long line"; short lines don't
- // count. (This flag is used to determine if a trailing blank line
- // should be added to the output)
- //
- *didOutput = true;
- }
- }
- }
- } // ProcessFile
-
- //----------------------------------------------------------------------------------------
- // Lurkers:
- //----------------------------------------------------------------------------------------
- void Lurkers(short vRefNum, long dirID, long startLurkingDirID)
- {
- OSErr err = noErr;
- short index = 1;
- Boolean didOutput = false;
-
- //
- // Build the pathname to this folder if full paths were
- // specified or if we're not doing short output
- //
- gFolderpath[0] = 0;
- if(OptionSpecified(kFullPathnames) || OptionSpecified(kShortOutput))
- BuildFolderPathname(vRefNum, dirID, gFolderpath, OptionSpecified(kShortOutput) && !OptionSpecified(kFullPathnames) ? startLurkingDirID : kMagicUnusedDirID, true);
-
- //
- // If doing long output, tell the user what we're doing
- //
- if(OptionSpecified(kShowProgress))
- {
- fflush(stdout);
- fprintf(stderr, "### Scanning folder \"%P\"\n", gFolderpath);
- }
-
- //
- // Get rid of the pathname if we're not going to output it.
- //
- //if(OptionSpecified(kFullPathnames) == false)
- // gFolderpath[0] = 0;
-
- //
- // Walk every file in this directory
- //
- while(err == noErr)
- {
- SpinCursor(1);
-
- gFilename[0] = 0;
-
- pb.hFileInfo.ioCompletion = nil;
- pb.hFileInfo.ioNamePtr = gFilename;
- pb.hFileInfo.ioResult = noErr;
- pb.hFileInfo.ioVRefNum = vRefNum;
- pb.hFileInfo.ioDirID = dirID;
- pb.hFileInfo.ioFDirIndex = index;
-
- err = PBGetCatInfo(&pb,false);
-
- //
- // We only care about files right now...
- //
- if((err == noErr) && ((pb.hFileInfo.ioFlAttrib & (1 << 4)) == 0))
- {
- //
- // If 'kTextOnly' is set, only process the file if its type
- // is 'TEXT'. Otherwise, always process the file
- //
- if( (OptionSpecified(kTextOnly) == false) || (pb.hFileInfo.ioFlFndrInfo.fdType == 'TEXT'))
- ProcessFile(vRefNum, dirID, gFolderpath, gFilename, &didOutput);
- }
-
- ++index;
- }
-
- //
- // Add another blank line if there was any output,
- // then flush stdout so that the text is actually
- // printed
- //
- if(didOutput)
- {
- putchar('\n');
- }
- fflush(stdout);
-
- //
- // Do we want to do a deep search?
- //
- if(OptionSpecified(kRecursive))
- {
- index = 1;
- err = noErr;
-
- //
- // Walk every folder in this directory
- //
- while(err == noErr)
- {
- gFilename[0] = 0;
-
- pb.dirInfo.ioCompletion = nil;
- pb.dirInfo.ioNamePtr = gFilename;
- pb.dirInfo.ioResult = noErr;
- pb.dirInfo.ioVRefNum = vRefNum;
- pb.dirInfo.ioDrDirID = dirID;
- pb.dirInfo.ioFDirIndex = index;
-
- err = PBGetCatInfo(&pb,false);
-
- //
- // Call Lurkers again on every folder we find...
- //
- if((err == noErr) && ((pb.hFileInfo.ioFlAttrib & (1 << 4)) != 0))
- {
- //
- // Don't do the recursive call if this folder's name
- // appears in the ignore list
- //
- if(ItemIsInIgnoreList(gFilename) == false)
- {
- Lurkers(vRefNum, pb.dirInfo.ioDrDirID, startLurkingDirID);
- }
- }
-
- ++index;
- }
- }
- } // Lurkers
-
-
- //----------------------------------------------------------------------------------------
- // main:
- //----------------------------------------------------------------------------------------
- main( int argc, char* argv[] )
- {
- Boolean optionsSpecified = false;
- Boolean didLurkers = false;
- short vRefNum = 0;
- long dirID = 0;
- short parms;
- short status = 0;
- Boolean* processedParameters;
- OSErr err = noErr;
-
- InitCursorCtl(nil);
-
- //
- // Set up the ignore list and the set of flags that indicates which
- // parameters we've processed. Both arrays only need to be 'argc'
- // elements in size, or smaller.
- //
- processedParameters = (Boolean*)NewPtr(sizeof(Boolean) * (argc + 1));
- gIgnoreList = (char**)NewPtr(sizeof(Ptr) * (argc + 1));
- for( parms = 0; parms < argc; ++parms )
- {
- processedParameters[parms] = false;
- gIgnoreList[parms] = nil;
- }
-
- //
- // Run through the parameters once looking for things that start with "-"
- //
- for( parms = 1; parms < argc; parms++ )
- {
- short length = strlen(argv[parms]);
-
- //
- // Look at all of the parameters that have a dash
- //
- if( argv[parms][0] == '-')
- {
- processedParameters[parms] = true;
-
- //
- // Process the specific flags
- //
- if( strcmp( argv[parms], "-modifiable" ) == 0 )
- {
- optionsSpecified = true;
- gWantOutputForState = kModifiedReadOnly | kCheckedOut | kNotInAProject;
- }
- else if( strcmp( argv[parms], "-notmodifiable" ) == 0 )
- {
- optionsSpecified = true;
- gWantOutputForState = kNotModifiable;
- }
- else if( strcmp( argv[parms], "-mro" ) == 0 )
- {
- optionsSpecified = true;
- gWantOutputForState = kModifiedReadOnly;
- }
- else if( strcmp( argv[parms], "-notinanyproject" ) == 0 )
- {
- optionsSpecified = true;
- gWantOutputForState = kNotInAProject;
- }
- else if( strcmp( argv[parms], "-insomeproject" ) == 0 )
- {
- optionsSpecified = true;
- gWantOutputForState = kModifiedReadOnly | kCheckedOut | kNotModifiable;
- }
- else if( strcmp( argv[parms], "-modifiableinproject" ) == 0 )
- {
- optionsSpecified = true;
- gWantOutputForState = kHasCKIDAndIsModifiable;
- }
- else if( strcmp( argv[parms], "-checkedoutmodifiable" ) == 0 )
- {
- optionsSpecified = true;
- gWantOutputForState = kHasCKIDAndIsCheckedOut;
- }
- else if( strcmp( argv[parms], "-r" ) == 0 )
- {
- optionsSpecified = true;
- gOptions |= kRecursive;
- }
- else if( strcmp( argv[parms], "-s" ) == 0 )
- {
- optionsSpecified = true;
- gOptions |= kShortOutput;
- }
- else if( strcmp( argv[parms], "-f" ) == 0 )
- {
- optionsSpecified = true;
- gOptions |= kFullPathnames;
- }
- else if( strcmp( argv[parms], "-p" ) == 0 )
- {
- optionsSpecified = true;
- gOptions |= kShowProgress;
- }
- else if( strcmp( argv[parms], "-q" ) == 0 )
- {
- optionsSpecified = true;
- gOptions |= kDontQuote;
- }
- else if( strcmp( argv[parms], "-rev" ) == 0 )
- {
- optionsSpecified = true;
- gOptions |= kOutputRevision;
- }
- else if( strcmp( argv[parms], "-textonly" ) == 0 )
- {
- optionsSpecified = true;
- gOptions |= kTextOnly;
- }
- else if( strcmp( argv[parms], "-ignore" ) == 0 )
- {
- optionsSpecified = true;
- ++parms;
- if(parms >= argc)
- {
- fprintf(stderr,"### %s - Encountered -ignore without a parameter", argv[0]);
- }
- else
- {
- processedParameters[parms] = true;
- gIgnoreList[gNumberOfIgnoreItems++] = argv[parms];
- }
- }
- else
- {
- fprintf(stderr,"### %s - \"%s\" is not an option.\n", argv[0], argv[parms]);
- status = 1;
-
- break;
- }
- }
- }
-
- //
- // If neither -f nor -s, then -f
- //
- if((OptionSpecified(kFullPathnames) == false) && (OptionSpecified(kShortOutput) == false))
- gOptions |= kFullPathnames;
-
- if(OptionSpecified(kShowProgress))
- {
- fprintf(stderr, "%s - Determine file's projector status.\n", argv[0]);
- fprintf(stderr, "\t©1994, 1997 Apple Computer, Inc. by Greg Anderson\n");
- }
-
- //
- // If all of the "-" parameters were processed okay, then run through
- // the parameters again looking for directory names
- //
- if(status == 0)
- {
- if((OptionSpecified(kShortOutput) == false))
- {
- putchar('\n');
- fflush(stdout);
- }
-
- for( parms = 1; parms < argc; parms++ )
- {
- //
- // If we didn't process this parameter the first time through the
- // loop, then we assume it is the name of a directory search
- // or a file to test
- //
- if(processedParameters[parms] == false)
- {
- short length = strlen(argv[parms]);
- FSSpec tempFSSpec;
-
- strcpy((char *) gFilename+1, argv[parms]);
- gFilename[0] = length;
-
- //
- // Make an FSSpec so that we can get the vRefNum of the specified
- // directory.
- //
- err = FSMakeFSSpec(0, 0, gFilename, &tempFSSpec);
- if(err == noErr)
- {
- vRefNum = tempFSSpec.vRefNum;
-
- //
- // Use PBGetCatInfo to get the dirID of the specified folder
- //
- pb.dirInfo.ioCompletion = nil;
- pb.dirInfo.ioNamePtr = (StringPtr) &tempFSSpec.name;
- pb.dirInfo.ioResult = noErr;
- pb.dirInfo.ioVRefNum = vRefNum;
- pb.dirInfo.ioDrDirID = tempFSSpec.parID;
- pb.dirInfo.ioFDirIndex = 0;
-
- err = PBGetCatInfo(&pb,false);
- }
-
- //
- // Run Lurkers as soon as we get a directory or filename
- // (this is an easy and sleazy way to support multiple
- // folders/files on the command line)
- //
- if(err == noErr)
- {
- //
- // Is this a file or a folder?
- //
- if((pb.hFileInfo.ioFlAttrib & (1 << 4)) != 0)
- {
- dirID = pb.dirInfo.ioDrDirID;
- if(ItemIsInIgnoreList(tempFSSpec.name) == false)
- {
- Lurkers(vRefNum, dirID, dirID);
- }
- }
- else
- {
- Boolean unusedDidOutput = false;
- dirID = pb.hFileInfo.ioFlParID;
-
- //
- // If 'kTextOnly' is set, only process the file if its type
- // is 'TEXT'. Otherwise, always process the file
- //
- if( (OptionSpecified(kTextOnly) == false) || (pb.hFileInfo.ioFlFndrInfo.fdType == 'TEXT'))
- {
- //
- // At this point, 'gFileName' contains the file exactly as
- // the user specified it. Copy the filename from the temporary
- // FSSpec, which contains only the name.
- //
- Size len = tempFSSpec.name[0] + 1;
- BlockMove((Ptr)tempFSSpec.name, (Ptr)gFilename, len);
-
- //
- // Build the pathname to this folder if full paths were specified
- //
- gFolderpath[0] = 0;
- if(OptionSpecified(kFullPathnames))
- {
- BuildFolderPathname(vRefNum, dirID, gFolderpath, kMagicUnusedDirID, true);
- }
- // fprintf(stderr, "### About to process <%P> in folder <%P> dirID <%ld>\n", gFilename, gFolderpath, dirID);
-
- ProcessFile(vRefNum, dirID, gFolderpath, gFilename, &unusedDidOutput);
- }
- }
-
- //
- // Make a note of the fact that we ran lurkers
- //
- didLurkers = true;
- }
- else
- {
- //
- // Set 'didLurkers' if we tried to run lurkers, too
- //
- fprintf(stderr, "### Error %d accessing %P\n", err, gFilename);
- didLurkers = true;
- }
- }
- }
- }
-
- //
- // if there were errors in the parameters, print usage
- //
- if((status == 1) || (didLurkers == false))
- {
- fprintf(stderr, usage, argv[0]);
-
- //
- // Even more usage information for Andy...
- //
- fprintf(stderr, "\t©1994, 1997 Apple Computer, Inc. by Greg Anderson\n");
- fprintf(stderr, "\n");
- fprintf(stderr, "\tFile's projector status:\n");
- fprintf(stderr, "\t\t-insomeproject\t\t\t# File is checked in to some project (has ckid)\n");
- fprintf(stderr, "\t\t-notinanyproject\t\t# File is not checked in to any project (no ckid)\n");
- fprintf(stderr, "\t\t-checkedoutmodifiable\t# File is checked in to some project and is\n");
- fprintf(stderr, "\t\t\t\t\t\t\t\t#\tcurrently checked out for modification (but not MRO'ed)\n");
- fprintf(stderr, "\t\t-modifiableinproject\t# Synonym for -checkedoutmodifiable\n");
- fprintf(stderr, "\t\t-modifiable\t\t\t\t# Not in a project, in a project and checked out for modification,\n");
- fprintf(stderr, "\t\t\t\t\t\t\t\t#\tor in a project and checked out modified-read-only\n");
- fprintf(stderr, "\t\t-notmodifiable\t\t\t# In a project and not modifiable\n");
- fprintf(stderr, "\t\t-mro\t\t\t\t\t# In a project and modified-read-only\n");
- fprintf(stderr, "\n");
- fprintf(stderr, "\tOptions:\n");
- fprintf(stderr, "\t\t-r\t\t\t\t\t\t# Recursive search\n");
- fprintf(stderr, "\t\t-s\t\t\t\t\t\t# Short output\n");
- fprintf(stderr, "\t\t-q\t\t\t\t\t\t# Never quote filenames\n");
- fprintf(stderr, "\t\t-f\t\t\t\t\t\t# Output full pathnames\n");
- fprintf(stderr, "\t\t-rev\t\t\t\t\t# Output file's projector revision number (if any)\n");
- fprintf(stderr, "\t\t-textonly\t\t\t\t# Ignore files whose type is not TEXT\n");
- fprintf(stderr, "\t\t-ignore n\t\t\t\t# Ignore files and folders whose name is \"n\"\n");
- fprintf(stderr, "\t\t-p\t\t\t\t\t\t# Show progress info\n");
- fprintf(stderr, "\n");
- fprintf(stderr, "\tSpecifying a file tests only that file. Specifying a directory tests every\n");
- fprintf(stderr, "\tfile in that directory (and every file anywhere in that hierarchy if -r is used).\n");
- }
-
- return status;
- } // main
-